home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / SourceCode / Palettes / PAThumbWheel / PAThumbWheelCellDrawing.m < prev    next >
Text File  |  1995-06-12  |  11KB  |  310 lines

  1. #import "PAThumbWheelCellDrawing.h"
  2.  
  3. /******************************************************************************
  4.     PAThumbWheelCellDrawing
  5.     
  6. This file handles all of the drawing for the PAThumbWheelCell. Don't try to make sense of any of this.
  7.  
  8. Copyright 1992, Jeff Martin. (jmartin@next.com 415-780-3833)
  9. ******************************************************************************/
  10. #define EQUAL(a,b) (ABS((a)-(b))<0.00001)
  11. #define NOTEQUAL(a,b) (ABS((a)-(b))>0.0001)
  12. #define ISBETWEEN(x,a,b) (((x)>=(a))&&((x)<=(b)))
  13. // A mod function for floating values
  14. #define MOD(x,y) ((x) - (y)*(int)((float)(x)/(y)))
  15. #define CLAMP_WITH_WRAP(a,x,y)                                              \
  16. ( ((a) < (x)) ? ((y) - MOD(((x)-(a)),((y)-(x)))) :                         \
  17. ( ((a) > (y)) ? ((x) + MOD(((a)-(y)),((y)-(x)))) : (a) ) )
  18. #define EVEN(x) (!(((int)(x))%2))
  19.  
  20.  
  21. @implementation PAThumbWheelCell(Drawing)
  22.  
  23. - drawSelf:(const NXRect *)theFrame inView:view
  24. {
  25.     NXRect frame = *theFrame;
  26.     float *pnts;                        // Used for user path of dashes
  27.     char *ops;                            // Used for user path of dashes
  28.     int pntCount, opCount;                // Used for user path of dashes
  29.     float bbox[4] = { NX_X(theFrame), NX_Y(theFrame),
  30.         NX_MAXX(theFrame), NX_MAXY(theFrame) };
  31.     
  32.     // Inset by two to allow for bezeled border
  33.     NXInsetRect(&frame,2,2);
  34.  
  35.     // Draw the background
  36.     if([self isLinear]) {
  37.         NXDrawGrayBezel(theFrame, theFrame);
  38.         NXSetColor(color);
  39.         NXRectFill(&frame);
  40.     }
  41.     else { // if([self isRadial])
  42.         NXSize imageSize;
  43.         [image getSize:&imageSize];
  44.         if(NOTEQUAL(imageSize.width,NX_WIDTH(theFrame)) ||
  45.             NOTEQUAL(imageSize.height,NX_HEIGHT(theFrame)))
  46.             [self generateImage:theFrame];
  47.         [image composite:NX_COPY toPoint:&theFrame->origin];
  48.     }
  49.     
  50.     // Get the userpath for the dashes
  51.     [self getDashesForFrame:&frame :&pnts :&pntCount :&ops :&opCount];
  52.  
  53.     // Draw dashes once for white part of groove
  54.     if([self isHorizontal]) PStranslate(1,0); else PStranslate(0,-1);
  55.  
  56.     // Draw linear white dashes
  57.     if([self isLinear]) {
  58.         NXSetColor(PAScaleRGBColor(color, 1.5));
  59.         DPSDoUserPath(pnts, pntCount, dps_float, ops, opCount, bbox, 
  60.             dps_ustroke);
  61.     }
  62.     
  63.     // Break up radial white dashes to fade a little bit at ends
  64.     else {
  65.         int i = 0, j = 0, d = [self isHorizontal]? 0 : 1;
  66.         float oneQuarterOfX = [self isHorizontal]? (NX_X(&frame) + 
  67.             NX_WIDTH(&frame)/4) : (NX_Y(&frame) + NX_HEIGHT(&frame)/4);
  68.         float threeQuartersOfX =[self isHorizontal]? (NX_X(&frame) + 
  69.             3*NX_WIDTH(&frame)/4) : (NX_Y(&frame) + 3*NX_HEIGHT(&frame)/4);
  70.  
  71.         NXSetColor(color);
  72.         while((i < pntCount) && (pnts[i+d] < oneQuarterOfX)) i += 4; j = i;
  73.         if(i>0) DPSDoUserPath(pnts, i, dps_float, ops, i/2, bbox, dps_ustroke);
  74.     
  75.         NXSetColor(PAScaleRGBColor(color, 1.5));
  76.         while((j < pntCount) && (pnts[j+d] < threeQuartersOfX)) j+=4;
  77.         if(j>i) DPSDoUserPath(&pnts[i], j-i, dps_float, &ops[i/2], j/2-i/2,
  78.             bbox, dps_ustroke);
  79.     
  80.         NXSetColor(color);
  81.         if(pntCount>j) DPSDoUserPath(&pnts[j], pntCount-j, dps_float, 
  82.             &ops[j/2], opCount-j/2, bbox, dps_ustroke);
  83.     }
  84.     
  85.     if([self isHorizontal]) PStranslate(-1,0); else PStranslate(0,1);
  86.  
  87.     // Draw again for dark part of groove
  88.     if([self isLinear]) NXSetColor(PAScaleRGBColor(color, .5)); 
  89.     else NXSetColor(NX_COLORBLACK);
  90.     DPSDoUserPath(pnts, pntCount, dps_float, ops, opCount, bbox, dps_ustroke);
  91.     
  92.     // If disabled then dim ThumbWheel out
  93.     if(![self isEnabled]) {
  94.         NXSetColor(NX_COLORWHITE); PSsetalpha(.5);
  95.         PScompositerect(NX_X(&frame), NX_Y(&frame), NX_WIDTH(&frame),
  96.             NX_HEIGHT(&frame), NX_SOVER);
  97.     }
  98.     
  99.     // Free user path variables
  100.     free(pnts); free(ops);
  101.     return self;
  102. }
  103.  
  104. - getDashesForFrame:(const NXRect *)frame :(float **)PNTS :(int *)PNTCOUNT :(char **)OPS :(int *)OPCOUNT
  105. {
  106.     // Get dashInterval and shift
  107.     int dashInt = [self dashInterval];
  108.     int shift = [self shift:frame];
  109.  
  110.     // Calculate how many dashes there will be and alloc space for pnts and ops
  111.     int dashCount = 2 + ([self isRadial] ? 180/dashInt : 
  112.         ([self isVertical] ? NX_HEIGHT(frame) : NX_WIDTH(frame)) / dashInt);
  113.     float *pnts = malloc(sizeof(float)*dashCount*4); // (moveto+lineto)*(x+y)=4
  114.     char *ops     = malloc(sizeof(char)*dashCount*2);  // (moveto+lineto) = 2
  115.     int i=0, j=0;
  116.     
  117.     // Calculate dash sizes
  118.     int dashBase     = [self isVertical] ? NX_X(frame) : NX_Y(frame);
  119.     int dashHeight    = [self isVertical] ? NX_WIDTH(frame) : NX_HEIGHT(frame);
  120.     int dashMinTop     = dashBase + dashHeight*.25;
  121.     int dashMajTop     = dashBase + dashHeight*.5;
  122.     int dashTop        = dashBase + dashHeight;
  123.     
  124.     float base        =    [self isVertical] ? NX_Y(frame) : NX_X(frame);
  125.     float width        =    [self isVertical]? NX_HEIGHT(frame) : NX_WIDTH(frame);
  126.     float halfWidth    =    width/2;
  127.     float mid        =    base + halfWidth;
  128.     float top        =    base + width;
  129.  
  130.     float mainDash;
  131.     float x;
  132.     
  133.     // Calculate whether first dash is a major one. 
  134.     BOOL isMajor    = (shift>=0)? EVEN(shift/dashInt) : !EVEN(shift/dashInt);
  135.         
  136.     // Calculate Linear dashes
  137.     if([self isLinear]) {
  138.         // Set Main dash
  139.         mainDash = base + shift;
  140.  
  141.         // Calculate starting point and set the dashes
  142.         x = base+CLAMP_WITH_WRAP(shift,0,dashInt)%((shift>=0)? dashInt:999999);
  143.         if([self isVertical]) while(x<top) {
  144.             pnts[i++] = dashBase; pnts[i++] = x;
  145.             pnts[i++] = isMajor ? dashMajTop : dashMinTop;
  146.             if(EQUAL(x, mainDash)&&[self showMainDash]) pnts[i-1] = dashTop;
  147.             pnts[i++] = x;
  148.             x += dashInt; isMajor = !isMajor;
  149.         }
  150.         else while(x<top) {
  151.             pnts[i++] = x; pnts[i++] = dashBase;
  152.             pnts[i++] = x; pnts[i++] = isMajor ? dashMajTop : dashMinTop;
  153.             if(EQUAL(x, mainDash)&&[self showMainDash]) pnts[i-1] = dashTop;
  154.             x += dashInt; isMajor = !isMajor;
  155.         }
  156.     }
  157.  
  158.     // Calculate Radial Dashes
  159.     else {
  160.         // This is used to convert the degrees of a dash to a location(in pnts)
  161.         float linDash = 0;
  162.         
  163.         // Inset dash size for beveled edges
  164.         dashBase++; dashTop--;
  165.         
  166.         // Calc Main dash if we show it and it is in sight
  167.         mainDash = mid - IntCos(shift)*halfWidth;
  168.  
  169.         // Calculate the starting point and set the dashes
  170.         x = CLAMP_WITH_WRAP(shift, 0, dashInt)%((shift>=0)? dashInt:999999);
  171.         if([self isVertical]) while(x<180) {
  172.             linDash = mid - IntCos(x)*halfWidth;
  173.             pnts[i++] = dashBase; pnts[i++] = linDash;
  174.             pnts[i++] = isMajor ? dashMajTop : dashMinTop;
  175.             // Check to see if this is a valid main dash
  176.             if(isMajor && EQUAL(linDash,mainDash) && [self showMainDash] &&
  177.                 ISBETWEEN(shift, 0, 180)) pnts[i-1]=dashTop;
  178.             pnts[i++] = linDash;
  179.             x += dashInt; isMajor = !isMajor;
  180.         }
  181.         else while(x<180) {
  182.             linDash = mid - IntCos(x)*halfWidth;
  183.             pnts[i++] = linDash; pnts[i++] = dashBase;
  184.             pnts[i++] = linDash; pnts[i++] = isMajor ? dashMajTop : dashMinTop;
  185.             // Check to see if this is a valid main dash
  186.             if(isMajor && EQUAL(linDash,mainDash) && [self showMainDash] &&
  187.                 ISBETWEEN(shift, 0, 180)) pnts[i-1]=dashTop;
  188.             x += dashInt; isMajor = !isMajor;
  189.         }
  190.     }
  191.         
  192.     // fill the ops array with dps_moveto and dps_lineto
  193.     while(j<i/2) { ops[j++] = dps_moveto; ops[j++] = dps_lineto; }
  194.      
  195.     // Set the passed in pointers to the arrays and the counts and return
  196.     *PNTS = pnts; *OPS = ops; *PNTCOUNT = i; *OPCOUNT = j;
  197.     return self;
  198. }
  199.  
  200. - generateImage:(const NXRect *)theFrame
  201. {
  202.     float circleHeight;
  203.     float x, rad, rad2, mid;
  204.     NXRect frame = *theFrame;
  205.  
  206.     if(!image) image = [[NXImage alloc] init];
  207.     [image setSize:&frame.size];
  208.     [image lockFocus];
  209.     NXDrawGrayBezel(&frame,&frame);
  210.     NXInsetRect(&frame,2,2);
  211.     PSsetlinewidth(0.0); 
  212.  
  213.     // Draw horizonal version
  214.     if(direction == DIRECTION_HORIZONTAL) {
  215.         rad = NX_WIDTH(&frame)/2.0; rad2 = rad*rad;
  216.         mid = NX_MIDX(&frame);
  217.  
  218.         for(x = NX_X(&frame); x <= NX_MIDX(&frame); x++) {
  219.             float topx = NX_MAXX(&frame) - (x - NX_X(&frame));
  220.             circleHeight = sqrt(rad2 - (mid-x)*(mid-x))/rad;
  221.  
  222.             PSnewpath();
  223.             NXSetColor(PAScaleRGBColor(color, .5*circleHeight));
  224.             PSmoveto(x, NX_Y(&frame));        PSlineto(x, NX_Y(&frame) + 2);
  225.             PSmoveto(topx, NX_Y(&frame));    PSlineto(topx, NX_Y(&frame) + 2);
  226.             PSstroke();
  227.  
  228.             PSnewpath();
  229.             NXSetColor(PAScaleRGBColor(color, circleHeight));
  230.             PSmoveto(x, NX_Y(&frame)+2);    PSlineto(x, NX_MAXY(&frame) - 1);
  231.             PSmoveto(topx, NX_Y(&frame)+2);PSlineto(topx,NX_MAXY(&frame) -1);
  232.             PSstroke();
  233.  
  234.             PSnewpath();
  235.             NXSetColor(PAScaleRGBColor(color,1.5*circleHeight));
  236.             PSmoveto(x, NX_MAXY(&frame)-1);    PSlineto(x, NX_MAXY(&frame));
  237.             PSmoveto(topx, NX_MAXY(&frame)-1);PSlineto(topx, NX_MAXY(&frame));
  238.             PSstroke();
  239.         }
  240.     }
  241.     else {
  242.         rad = NX_HEIGHT(&frame)/2.0; rad2 = rad*rad;
  243.         mid = NX_MIDY(&frame);
  244.         for(x = NX_Y(&frame); x <= NX_MIDY(&frame); x++) {
  245.             float topx = NX_MAXY(&frame) - (x - NX_Y(&frame));
  246.             circleHeight = sqrt(rad2 - (mid-x)*(mid-x))/rad;
  247.  
  248.             PSnewpath();
  249.             NXSetColor(PAScaleRGBColor(color, 1.5*circleHeight));
  250.             PSmoveto(NX_X(&frame), x);        PSlineto(NX_X(&frame) + 2, x);
  251.             PSmoveto(NX_X(&frame), topx);    PSlineto(NX_X(&frame) + 2, topx);
  252.             PSstroke();
  253.  
  254.             PSnewpath();
  255.             NXSetColor(PAScaleRGBColor(color, circleHeight));
  256.             PSmoveto(NX_X(&frame)+2, x);    PSlineto(NX_MAXX(&frame) - 1, x);
  257.             PSmoveto(NX_X(&frame)+2, topx);    PSlineto(NX_MAXX(&frame) - 1,topx);
  258.             PSstroke();
  259.  
  260.             PSnewpath();
  261.             NXSetColor(PAScaleRGBColor(color, .5*circleHeight));
  262.             PSmoveto(NX_MAXX(&frame)-1, x);    PSlineto(NX_MAXX(&frame), x);
  263.             PSmoveto(NX_MAXX(&frame)-1, topx);PSlineto(NX_MAXX(&frame), topx);
  264.             PSstroke();
  265.         }
  266.     }
  267.  
  268.     [image unlockFocus];
  269.     return self;
  270. }
  271.  
  272. - (const char *)getInspectorClassName { return "PAThumbWheelInspector"; }
  273.  
  274. @end
  275.  
  276. NXColor PAScaleRGBColor(NXColor c, float scale)
  277. { return NXConvertRGBToColor(NXRedComponent(c)*scale, NXGreenComponent(c)*
  278. scale, NXBlueComponent(c)*scale); }
  279.  
  280. float _IntSin[91] ={0, 0.017452406, 0.034899497, 0.052335956, 0.069756474, 
  281.   0.087155743, 0.10452846, 0.12186934, 0.1391731, 
  282.   0.15643447, 0.17364818, 0.190809, 0.20791169, 
  283.   0.22495105, 0.2419219, 0.25881905, 0.27563736, 
  284.   0.2923717, 0.30901699, 0.32556815, 0.34202014, 
  285.   0.35836795, 0.37460659, 0.39073113, 0.40673664, 
  286.   0.42261826, 0.43837115, 0.4539905, 0.46947156, 
  287.   0.48480962, 0.5, 0.51503807, 0.52991926, 0.54463904, 
  288.   0.5591929, 0.57357644, 0.58778525, 0.60181502, 
  289.   0.61566148, 0.62932039, 0.64278761, 0.65605903, 
  290.   0.66913061, 0.68199836, 0.69465837, 0.70710678, 
  291.   0.7193398, 0.7313537, 0.74314483, 0.75470958, 
  292.   0.76604444, 0.77714596, 0.78801075, 0.79863551, 
  293.   0.80901699, 0.81915204, 0.82903757, 0.83867057, 
  294.   0.8480481, 0.8571673, 0.8660254, 0.87461971, 
  295.   0.88294759, 0.89100652, 0.89879405, 0.90630779, 
  296.   0.91354546, 0.92050485, 0.92718385, 0.93358043, 
  297.   0.93969262, 0.94551858, 0.95105652, 0.95630476, 
  298.   0.9612617, 0.96592583, 0.97029573, 0.97437006, 
  299.   0.9781476, 0.98162718, 0.98480775, 0.98768834, 
  300.   0.99026807, 0.99254615, 0.9945219, 0.9961947, 
  301.   0.99756405, 0.99862953, 0.99939083, 0.9998477, 1.};
  302.  
  303. float IntSin(int x)
  304. {
  305.     if(x < 0)   return -IntSin(-x);
  306.     if(x > 180) return -IntSin(x%180);
  307.     if(x > 90)  return  _IntSin[180-x];
  308.     return _IntSin[x];
  309. }
  310.